#include "ssh.h"

void SSHTables::Template::Set(string o, string s) {
	for (string::size_type pos(0); pos != string::npos; pos += s.length()) {
		if ((pos = code.find(o, pos)) != string::npos) {
			code.replace(pos, o.length(), s);
		} else {
			break;
		}
	}
}

string SSHTables::Template::GetCode() const {
	return code;
}

SSHTables::Template* SSHTables::Template::Update(string o, string s) {
	o = "[:" + o + "]";
	Set(o, s);
	return this;
}
SSHTables::Template::Template() {
	time_t t = time(0); 
    char tmp[64]; 
    strftime(tmp, sizeof(tmp), "%Y/%m/%d %X %A", localtime(&t)); 
	code = "//======================================================================  \n"
			"//\n"
			"//        Copyright (C) " + string(tmp) + ", Yao Peng   \n"
			"//        All rights reserved\n"
			"//\n"
			"//        filename : ssh\n"
			"//        description : the code is automatically generated by\n"
			"//	       'CodeOffice' Application for database tables using SSH framework\n"
			"//\n"
			"//        mail : yaopengdn@126.com\n"
			"//        http://weibo.com/u/2151926144 \n"
			"//\n"
			"//======================================================================\n";
}

SSHTables::DomainTemplate::DomainTemplate(int size) : Template(){
	code += string("package [:package].domain;\n\n") +							
			"import javax.persistence.*;\n\n"  +
			"@Entity\n"	 +		
			"@Table(name=\"[:name]\", schema=\"[:schema]\")\n" +
			"public class [:objectName] { \n" +
				"\t@Id \n" +
				"\t@GeneratedValue(strategy=GenerationType.AUTO)\n" +
				"\tprivate int id;\n";
	string data = "", function = "", constructorName = "\tpublic [:objectName](int id, ", constructorText = "{\n\t\tthis.id = id;\n";
	for (int i = 0; i < size; i++) {
		char index[25];
		itoa(i, index, 10);
		string column = "column" + string(index);
		string type = column + "_type", name = column + "_name_s", Name = column + "_name";
		constructorName += "[:" + type + "] [:" + name + "], ";
		constructorText += "\t\tthis.[:" + name + "] = [:" + name + "];\n";
		data += string("\t@Column\n") +
				"\tprivate [:" + type + "] [:" + name + "];\n";
		function += string("\tpublic [:" + type + "] get[:" + Name + "]() {\n") +
			"\t\treturn this.[:" + name + "];\n\t}\n\n";
		function += string("\tpublic void set[:" + Name + "]([:" + type + "] [:" + name + "]) {\n") +
			"\t\tthis.[:" + name + "] = [:" + name + "];\n\t}\n\n";
	}
	data += "\n";
	constructorName = constructorName.substr(0,  constructorName.length()-2) + ") ";
	constructorText += "\t}\n\n";
	code += data + "\tpublic [:objectName]() {\n\t}\n\n" + constructorName + constructorText + "\tpublic void setId(int id) {\n" +
						"\t\tthis.id = id;\n" +
					"\t}\n\n" +
					"\tpublic int getId() {\n" +
						"\t\treturn id;\n" +
					"\t}\n\n" +
					function + "}\n";
}

SSHTables::DAOTemplate::DAOTemplate(int size) {
	code += string("package [:package].dao;\n\n") +							
			"import java.util.List;\nimport [:package].domain.[:objectName];\n\n" +
			"public interface [:objectName]DAO {\n" +
			"\tvoid create([:objectName] [:sobjectName]);\n" +
			"\tvoid update([:objectName] [:sobjectName]);\n" +
			"\tvoid delete([:objectName] [:sobjectName]);\n" +
			"\tList<[:objectName]> queryList();\n}\n";
}

SSHTables::ServiceTemplate::ServiceTemplate(int size) {
	code += string("package [:package].service;\n\n") +							
			"import java.util.List;\nimport [:package].domain.[:objectName];\n\n" +
			"public interface [:objectName]Service {\n" +
			"\tboolean create([:objectName] [:sobjectName]);\n" +
			"\tboolean update([:objectName] [:sobjectName]);\n" +
			"\tboolean delete([:objectName] [:sobjectName]);\n" +
			"\tList<[:objectName]> queryList();\n}\n";
}

SSHTables::ServiceImplTemplate::ServiceImplTemplate(int size) {
	code += string("package [:package].service.impl;\n\n") +

			"import java.util.List;\n"
			"import javax.annotation.Resource;\n"
			"import org.springframework.stereotype.Component;\n"
			"import [:package].dao.[:objectName]DAO;\n"
			"import [:package].domain.[:objectName];\n"
			"import [:package].service.[:objectName]Service;\n\n"

			"@Component(\"[:sobjectName]Service\")\n"
			"public class [:objectName]ServiceImpl implements [:objectName]Service {\n"
				"\tprivate [:objectName]DAO [:sobjectName]DAO;\n"
	
				"\tpublic [:objectName]DAO get[:objectName]DAO() {\n"
					"\t\treturn this.[:sobjectName]DAO;	\n"
				"\t}\n\n"
	
				"\t@Resource(name=\"[:sobjectName]DAO\")\n"
				"\tpublic [:objectName]Service set[:objectName]DAO([:objectName]DAO [:sobjectName]DAO) {\n"
					"\t\tthis.[:sobjectName]DAO = [:sobjectName]DAO;\n"
					"\t\treturn this;\n"
				"\t}\n\n"
				"\t@Override\n"
				"\tpublic boolean create([:objectName] [:sobjectName]) {\n"
					"\t\tthis.get[:objectName]DAO().create([:sobjectName]);\n"
					"\t\treturn true;\n"
				"\t}\n\n"

				"\t@Override\n"
				"\tpublic boolean update([:objectName] [:sobjectName]) {\n"
					"\t\tthis.get[:objectName]DAO().update([:sobjectName]);\n"
					"\t\treturn true;\n"
				"\t}\n\n"

				"\t@Override\n"
				"\tpublic boolean delete([:objectName] [:sobjectName]) {\n"
					"\t\tthis.get[:objectName]DAO().delete([:sobjectName]);\n"
					"\t\treturn true;\n"
				"\t}\n\n"

				"\t@Override\n"
				"\tpublic List<[:objectName]> queryList() {\n"
					"\t\treturn this.get[:objectName]DAO().queryList();\n"
				"\t}\n"
			"}\n";
}
SSHTables::DAOImplTemplate::DAOImplTemplate(int size) {
	code += string("package [:package].dao.impl;\n\n") +

			"import java.util.List;\n"
			"import java.util.logging.Logger;\n"
			"import javax.annotation.Resource;\n"
			"import org.hibernate.Session;\n"
			"import org.hibernate.SessionFactory;\n"
			"import org.springframework.stereotype.Component;\n"
			"import [:package].dao.[:objectName]DAO;\n"
			"import [:package].domain.[:objectName];\n"

			"@Component(\"[:sobjectName]DAO\")\n"
			"public class [:objectName]DAOImpl implements [:objectName]DAO {\n"
				"\tprivate String hql = \"from [:objectName]\";\n"
				"\tstatic Logger logger = Logger.getLogger([:objectName]DAOImpl.class.getName());\n"
				"\tprivate SessionFactory sessionFactory;\n\n"

				"\tpublic SessionFactory getSessionFactory() {\n"
					"\t\treturn sessionFactory;\n"
				"\t}\n"
				"\t@Resource(name = \"sessionFactory\")\n"
				"\tpublic void setSessionFactory(SessionFactory sessionFactory) {\n"
					"\t\tthis.sessionFactory = sessionFactory;\n"
				"\t}\n\n"
	
				"\t@Override\n"
				"\tpublic void create([:objectName] [:sobjectName]) {\n"
					"\t\tSession s = sessionFactory.getCurrentSession(); \n"
					"\t\ts.beginTransaction();\n"
					"\t\ts.save([:sobjectName]);\n"
					"\t\ts.getTransaction().commit();\n"
				"\t}\n\n"

				"\t@Override\n"
				"\tpublic void update([:objectName] [:sobjectName]) {\n"
					"\t\tSession s = sessionFactory.getCurrentSession();\n" 
					"\t\ts.beginTransaction();\n"
					"\t\ts.update([:sobjectName]);\n"
					"\t\ts.getTransaction().commit();\n"
				"\t}\n\n"

				"\t@Override\n"
				"\tpublic void delete([:objectName] [:sobjectName]) {\n"
					"\t\tSession s = sessionFactory.getCurrentSession();\n"
					"\t\ts.beginTransaction();\n"
					"\t\ts.delete([:sobjectName]);\n"
					"\t\ts.getTransaction().commit();\n"
				"\t}\n\n"
				"\t@SuppressWarnings(\"unchecked\")\n"
				"\t@Override\n"
				"\tpublic List<[:objectName]> queryList() {\n"
					"\t\tSession s = sessionFactory.getCurrentSession();\n"
					"\t\ts.beginTransaction();\n"
					"\t\tList<[:objectName]> list = null;\n"
					"\t\ttry {\n"
					"\t\t\tlist = s.createQuery(hql).list();\n"
					"\t\t} catch(Exception e){\n"
					"\t\t\te.printStackTrace();\n"
					"\t\t}\n"
					"\t\ts.getTransaction().commit();\n"
					"\t\treturn list;\n"
				"\t}\n"
			"}\n";
}

SSHTables::SSHTables(string packagePath, const vector<Table>& tables) : packagePath(packagePath), tables(tables), package(GetPackage()){
}

string SSHTables::GetPackage() const{
	string temp(packagePath.rbegin(),packagePath.rend());

	int pos = temp.find("\\", 0);
	if (pos != string::npos) {
		pos = temp.find("\\", pos + 1);
		if (pos != string::npos) {
			pos = temp.length() - pos;
		} else {
			cout << "the package-path is not right.\n";
		}
	} else {
		cout << "the package-path is not right.\n";
	}
	string package = packagePath.substr(pos, packagePath.length());
	return package.replace(package.find("\\"), 1, ".");
}

void SSHTables::Make() {	
	string domain = packagePath + "\\domain\\";
	string dao = packagePath + "\\dao\\";
	string daoImpl = packagePath + "\\dao\\impl\\";
	string service = packagePath + "\\service\\";
	string serviceImpl = packagePath + "\\service\\impl\\";

	for (vector<Table>::const_iterator it = tables.begin(); it != tables.end(); it++) {
		CreateDomain(domain, *it);
		CreateDAO(dao, *it);
		CreateDAOImpl(daoImpl, *it);
		CreateService(service, *it);
		CreateServiceImpl(serviceImpl, *it);
	}
}

void SSHTables::CreateDomain(string path, const Table& table) const {
	ofstream fp;
	path += table.objectName +".java";
	fp.open(path);

	if (!fp.is_open()) {
		cout << "Could not create file:" + path + ". The directory doesn't exist?\n\n";
		return;
	}

	Template *t = new DomainTemplate(table.columns.size());
	t->Update("package", package)->Update("name", table.name)->Update("schema", table.schema)->Update("objectName", table.objectName);
	for (int i = 0; i < table.columns.size(); i++) {
		char index[25];
		itoa(i, index, 10);
		string column = "column" + string(index);
		string type = column + "_type", name = column + "_name_s", Name = column + "_name";
		t->Update(type, table.columns[i].type);
		
		string s = table.columns[i].name;
		t->Update(name, s);
		if (s[0] >= 'a' && s[0] <= 'z') {
			s[0] -= 32;
		}
		t->Update(Name, s);
	}

	string code = t->GetCode();
	delete t;
	Write(fp, code);
	fp.flush();
	fp.clear();
	fp.close();
}

void SSHTables::CreateDAO(string path, const Table& table) const {
	ofstream fp;
	path += table.objectName +"DAO.java";
	fp.open(path);

	if (!fp.is_open()) {
		cout << "Could not create file:" + path + ". The directory doesn't exist?\n\n";
		return;
	}

	Template *t = new DAOTemplate(table.columns.size());
	string s = table.objectName;
	s[0] += 32;
	t->Update("package", package)->Update("objectName", table.objectName)->Update("sobjectName", s);
	string code = t->GetCode();
	delete t;
	Write(fp, code);
	fp.flush();
	fp.clear();
	fp.close();
}

void SSHTables::CreateService(string path, const Table& table) const {
	ofstream fp;
	path += table.objectName +"Service.java";
	fp.open(path);

	if (!fp.is_open()) {
		cout << "Could not create file:" + path + ". The directory doesn't exist?\n\n";
		return;
	}

	Template *t = new ServiceTemplate(table.columns.size());
	string s = table.objectName;
	s[0] += 32;
	t->Update("package", package)->Update("objectName", table.objectName)->Update("sobjectName", s);
	string code = t->GetCode();
	delete t;
	Write(fp, code);
	fp.flush();
	fp.clear();
	fp.close();
}

void SSHTables::CreateDAOImpl(string path, const Table& table) const {
	ofstream fp;
	path += table.objectName +"DAOImpl.java";
	fp.open(path);

	if (!fp.is_open()) {
		cout << "Could not create file:" + path + ". The directory doesn't exist?\n\n";
		return;
	}

	Template *t = new DAOImplTemplate(table.columns.size());
	string s = table.objectName;
	s[0] += 32;
	t->Update("package", package)->Update("objectName", table.objectName)->Update("sobjectName", s)->Update("name", table.name);
	string code = t->GetCode();
	delete t;
	Write(fp, code);
	fp.flush();
	fp.clear();
	fp.close();
}

void SSHTables::CreateServiceImpl(string path, const Table& table) const {
	ofstream fp;
	path += table.objectName +"ServiceImpl.java";
	fp.open(path);

	if (!fp.is_open()) {
		cout << "Could not create file:" + path + ". The directory doesn't exist?\n\n";
		return;
	}

	Template *t = new ServiceImplTemplate(table.columns.size());
	string s = table.objectName;
	s[0] += 32;
	t->Update("package", package)->Update("objectName", table.objectName)->Update("sobjectName", s);
	string code = t->GetCode();
	delete t;
	Write(fp, code);
	fp.flush();
	fp.clear();
	fp.close();
}

void SSHTables::Write(ofstream& fp, string text) const{
	fp.write(text.c_str(), text.length());
}

bool SSHTablesConfig::Parse(string xmlFile) {
	TiXmlDocument *doc = new TiXmlDocument(xmlFile.c_str());
    if (doc->LoadFile()) {
		TiXmlElement *root = doc->RootElement();
		TiXmlElement *pack = root->FirstChildElement();
		while (pack != NULL) {
			if (string(pack->Value()) == "package-path") {
				packagePath = pack->GetText();
			}

			if (string(pack->Value()) == "tables") {
				TiXmlElement *t = pack->FirstChildElement();
				while (t != NULL) {
					if (string(t->Value()) == "table") {
						Table table;
						TiXmlElement *tb = t->FirstChildElement();
						while (tb != NULL) {	
							if (string(tb->Value()) == "name") {
								table.name = tb->GetText();
							}
							if (string(tb->Value()) == "schema") {
								table.schema = tb->GetText();
							}
							if (string(tb->Value()) == "objectName") {
								table.objectName = tb->GetText();
							}

							if (string(tb->Value()) == "columns") {
								TiXmlElement *col = tb->FirstChildElement();
								while (col != NULL) {
									if (string(col->Value()) == "column") {
										Column column = {"undefined", "undefined"};
										column.name = col->GetText();
										TiXmlAttribute *attr = col->FirstAttribute();
										while (attr != NULL) {
											if (string(attr->Name()) == "type") {
												column.type = attr->Value();
											}
											attr = attr->Next();
										}
										table.columns.push_back(column);
									}
									col = col->NextSiblingElement();
								}
							}
							tb = tb->NextSiblingElement();
						}
						t = t->NextSiblingElement();
						tables.push_back(table);
					}
				}
			}
			pack = pack->NextSiblingElement();
		}
		return true;
	}  
	return false;
}

const vector<Table>& SSHTablesConfig::GetTables() const {
	return tables;
}

string SSHTablesConfig::GetPackagePath() const {
	return packagePath;
}

